#include "backup.h"
#include <filesystem>
#include <fstream>
#include <iostream>
#include <openssl/sha.h> 

namespace fs = std::filesystem;

void FullBackup(const std::string &srcDir, const std::string &backupDir) {
    try {
        fs::path srcPath(srcDir);
        fs::path backupPath(backupDir);

        // Create backup directory if it does not exist
        fs::create_directories(backupPath);
        std::ofstream metaFile(backupPath / "metadata.txt");
        // Copy all files and directories from srcDir to backupDir
        for (const auto &entry : fs::recursive_directory_iterator(srcPath)) {
            const auto &path = entry.path();
            auto relativePath = path.lexically_relative(srcPath);
            fs::copy(path, backupPath / relativePath, fs::copy_options::overwrite_existing | fs::copy_options::recursive);
            metaFile<< "added && " << path.filename().string() << "\n";
        }
        metaFile.close();

        std::cout << "Full backup completed successfully.\n";
    } catch (const fs::filesystem_error &err) {
        std::cerr << "Filesystem error: " << err.what() << '\n';
    }
}
// 函数用于计算文件的 SHA256 哈希值  
std::string file_sha256(const std::string& filepath) {  
    unsigned char hash[SHA256_DIGEST_LENGTH];  
    SHA256_CTX sha256;  
    SHA256_Init(&sha256);  
  
    std::ifstream file(filepath, std::ifstream::binary);  
    std::stringstream ss;  
  
    if (file.is_open()) {  
        std::string buf;  
        while (file.good()) {  
            std::getline(file, buf);  
            SHA256_Update(&sha256, buf.data(), buf.size());  
        }  
        file.close();  
    } else {  
        throw std::runtime_error("Failed to open file");  
    }  
  
    SHA256_Final(hash, &sha256);  
  
    std::stringstream hexString;  
    for(int i = 0; i < SHA256_DIGEST_LENGTH; i++) {  
        hexString << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];  
    }  
  
    return hexString.str();  
}  
  
// 函数用于比较两个文件的 SHA256 哈希值  
bool compare_files_sha256(const std::string& filepath1, const std::string& filepath2) {  
    try {  
        std::string hash1 = file_sha256(filepath1);  
        std::string hash2 = file_sha256(filepath2);  
  
        return hash1 == hash2;  
    } catch (const std::exception& e) {  
        std::cerr << "Error: " << e.what() << std::endl;  
        return false;  
    }  
}  
void DiffBackup(const std::string &srcDir, const std::string &backupDir) {
    try {
        fs::path srcPath(srcDir);
        fs::path backupPath(backupDir);

        // Create backup directory if it does not exist
        fs::create_directories(backupPath);
        std::ofstream metaFile(backupPath / "metadata.txt");
        // Compare directories and identify changes
        for (const auto &entry : fs::recursive_directory_iterator(srcPath)) {
            const auto &path = entry.path();
            auto relativePath = path.lexically_relative(srcPath);
            auto backupFilePath = backupPath / relativePath;
            if (!fs::exists(backupFilePath)  ) {
                metaFile << "added && " << relativePath.string() << "\n";
                fs::copy(path, backupFilePath, fs::copy_options::overwrite_existing | fs::copy_options::recursive);
            
            }else if(!compare_files_sha256(backupFilePath,path)){
                metaFile << "modified && " << relativePath.string() << "\n";
                fs::copy(path, backupFilePath, fs::copy_options::overwrite_existing | fs::copy_options::recursive);
            
            }
        }
        for (const auto &entry : fs::recursive_directory_iterator(backupPath)) {
            const auto &path = entry.path();
            auto relativePath = path.lexically_relative(backupPath);
            auto srcFilePath = srcPath / relativePath;
            if(relativePath.string()=="metadata.txt"){
                continue;
            }
            if (!fs::exists(srcFilePath)  ) {
                metaFile << "deleted && " << relativePath.string() << "\n";
            }
        }
        metaFile.close();

        std::cout << "Differential backup completed successfully.\n";
    } catch (const fs::filesystem_error &err) {
        std::cerr << "Filesystem error: " << err.what() << '\n';
    }
}
